const axios = require('axios')
const qs = require('qs')
const {cronValidate} = require('./tools-public-cron')
const {encodeGb2312, decodeGb2312} = require('./tools-public-gb2312')

// 缓存池(ajaxConstructor用)
let ENV = {}
if (typeof window !== 'undefined' && window.navigator) {
  ENV = window
} else if (typeof process !== 'undefined' && process.versions && process.versions.node) {
  ENV = global
}
ENV.CACHES = {}
ENV.getStorageCount = 0
ENV.cacheExpire = 60000

/**
 * JSON字符串格式化
 * @param {String} jsonStr
 * @param {*} defaultValue
 */
const jsonParse = (jsonStr, defaultValue) => {
  let result = defaultValue || {}
  if (jsonStr) {
    try {
      result = JSON.parse(jsonStr)
    } catch (err) {
      if (err && err.message === `Unexpected token ' in JSON at position 1`) {
        try {
          result = eval('(' + jsonStr + ')')
        } catch (err) {
          console.log('jsonParse:', err)
          result = defaultValue || {}
        }
      } else {
        result = defaultValue || {}
      }
    }
  }
  return result
}

/**
 * axios方法封装
 * @param {String} config 配置
 * @param {String} contentType Content-Type配置
 */
const ajaxConstructor = (config = {}, contentType) => {
  // 初始化配置
  let baseConfig = {
    cache: false, // 是否开启缓存
    cacheParams: '', // 传参标记
    cacheExpire: ENV.cacheExpire, // 过期时间，默认一分钟
    storage: false, // 是否开启本地缓存
    timeout: 180000,
    withCredentials: true, // 跨域携带cookies
    headers: {
      'Cache-Control': 'no-cache',
      'X-Requested-With': 'XMLHttpRequest',
      'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
    },
    paramsSerializer: function (params) {
      return qs.stringify(params, { arrayFormat: 'brackets' })
    },
    transformResponse: [function (data) {
      return data
    }]
  }
  // 融合自定义配置
  for (let k in (config || {})) {
    if (['headers'].includes(k)) {
      baseConfig.headers = { ...baseConfig.headers, ...config.headers }
    } else {
      baseConfig[k] = config[k]
    }
  }
  // 单独的contentType参数优先级更高
  if (contentType === 'json') {
    baseConfig.headers['Content-Type'] = 'application/json; charset=UTF-8'
  } else if (contentType === 'form') {
    baseConfig.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
  } else if (contentType) {
    baseConfig.headers['Content-Type'] = contentType
  }
  let contentTypeHandler = (headers, type, value) => {
    let name = ''
    if (!(headers instanceof Object)) return ''
    for (let k in (headers || {})) {
      if (String(k).toLocaleLowerCase() === 'content-type') name = k
    }
    if (!name) return ''
    if (type === 'set') {
      headers[name] = value
      return headers
    } else {
      return headers[name] || ''
    }
  }
  let requestPlugin = (config = {}) => {
    config.source = axios.CancelToken.source() // 设置取消函数
    config.cancelToken = config.source.token
    if (!config.params) config.params = {}
    if (config.method === 'post') {
      if (!config.data) config.data = {}
      if (
        (typeof (FormData) !== 'undefined' && config.data instanceof FormData) ||
        contentTypeHandler(config.headers).indexOf('application/json') > -1
      ) {
        // 不执行操作
      } else if (config.qs === undefined || config.qs) {
        config.data = qs.stringify(config.data)
      }
    }
    // 检查缓存
    if (config.storage && !ENV.getStorageCount) {
      ENV.CACHES = jsonParse(localStorage.getItem('jstoolsCommonCaches'))
      ENV.getStorageCount++
    }
    if (config.cache) {
      let expire = new Date().getTime()
      let key = `${config.url}${config.cacheParams || ''}`
      let data = ENV.CACHES[key]
      // 判断缓存数据是否存在，存在的话，是否过期，没过期就返回
      if (data && (expire - (data || {}).expire < (config.cacheExpire || ENV.cacheExpire))) {
        config.source.cancel((data || {}).data)
      }
    }
    return config
  }
  let responsePlugin = (response) => {
    response.config.source = null
    if (contentTypeHandler(response.headers).indexOf('application/json') > -1) {
      try {
        response.data = JSON.parse(response.data)
      } catch (e) {
        response.data = { code: 33, msg: '服务端返回数据格式错误！' }
      }
    }
    let errMessageDict = {
      300: '资源已被转移至其他位置',
      301: '请求的资源已被永久移动到新URI',
      302: '请求的资源已被临时移动到新URI',
      305: '请求的资源必须通过代理访问',
      400: '错误资源访问请求',
      401: '资源访问未授权',
      403: '资源禁止访问',
      404: '未找到要访问的资源',
      405: '请更换请求方法',
      406: '无法访问',
      408: '请求超时',
      413: '请求实体过大',
      414: '请求URI过长',
      500: '内部服务器错误',
      501: '未实现',
      503: '服务无法获得',
      504: '接口访问超时'
    }
    let errCode = isNaN(response.status) ? 1 : response.status
    let errMessage = `请求服务过程中发生错误【${errCode}】`
    if (response.status === 200) {
      // 缓存数据
      if (response.config.cache) {
        let key = `${response.config.url}${response.config.cacheParams || ''}`
        let data = {
          expire: new Date().getTime(),
          data: response
        }
        ENV.CACHES[key] = data
      }
      if (config.storage) {
        localStorage.setItem('jstoolsCommonCaches', JSON.stringify(ENV.CACHES))
      }
      return response
    } else if ((response.status < 200 || response.status >= 300) && errMessageDict[String(response.status)]) {
      errMessage = `${errMessageDict[String(response.status)]}【${errCode}】`
    }
    response.data = {
      code: 34,
      msg: errMessage || `请求服务过程中发生错误【${errCode}】`
    }
    contentTypeHandler(response.headers, 'set', 'application/json')
    return response
  }
  let instance = axios.create(baseConfig)
  // 并发请求
  instance.all = axios.all
  // 分隔请求
  instance.spread = axios.spread
  // 添加请求拦截器
  instance.interceptors.request.use(requestPlugin, (error) => {
    return Promise.reject(error)
  })
  // 添加响应拦截器
  instance.interceptors.response.use(responsePlugin, (error) => {
    if (axios.isCancel(error)) {
      return Promise.resolve(error.message.data)
    }
    return Promise.reject(error)
  })
  return instance
}

/**
 * 获得一个axios实例（application/x-www-form-urlencoded）
 */
const ajax = ajaxConstructor()

/**
 * 获得一个axios实例（application/json）
 */
const ajaxJson = (() => {
  let result = ajaxConstructor({}, 'json')
  result.get = ajax.get
  result.delete = ajax.delete
  return result
})()

/**
 * axios返回错误信息处理
 */
const ajaxError = (error, errorMessage) => {
  let message = ''
  if (errorMessage) {
    message = errorMessage
  } else if (typeof (error) === 'string') {
    message = error
  } else if (typeof (Error) !== 'undefined' && error instanceof Error) {
    message = error.message
  } else {
    message = '请求过程中出现问题，请稍后再试【000】'
  }
  let offLine = message === 'Network Error'
  if (typeof navigator === 'object') {
    offLine = (!navigator.onLine) || message === 'Network Error'
  }
  message = offLine ? '网络已断开连接，请连接网络后重试！' : message
  return message
}

/**
 * 获得一个下载实例
 * @param {Object} config 配置
 */
const downloadInstance = (config = {}) => {
  config = config || {}
  return axios.create({
    responseType: 'arraybuffer',
    timeout: 1000*60*60,
    headers: {
      'Cache-Control': 'no-cache',
      'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'
    },
    ...config
  })
}

/**
 * 核实URL地址是否可用
 * @param {String} url
 * @param {Number} timeout 请求超时时间
 * @returns {Object} URL的相关信息（无可用信息则返回null）
 */
const checkUrlStatus = (url, timeout = 30000) => {
  let result = null
  if (!url) return Promise.resolve(result)
  return new Promise((resolve, reject) => {
    let source = axios.CancelToken.source()
    setTimeout(() => { source.cancel() }, timeout)
    // 请求url地址如果包含中文将其转码后访问
    axios.head(encodeURI(url), { cancelToken: source.token }).then((res) => {
      result = {url, status: res.status, message: '地址可用', success: true}
    }).catch(err => {
      result = {url, status: (err.response || {}).status, message: err.message, success: false}
    }).finally(() => {
      resolve(result)
    })
  })
}

/**
 * 字符串枚举
 * @param {Object} config 配置
 * @param {Array} config/template 模板字符串数组，变量以{{}}标记
 * @param {Array} config/* 其它参数为模板字符串各个变量的枚举字典
 */
const enumStringByTemplate = (config) => {
  config = config || {}
  let template = config.template || []
  let result = [...template]
  let keys = Object.keys(config).filter(k => k !== 'template')
  keys = [...new Set(keys)]
  function fn (temps, key, values) {
    let result = []
    temps.forEach(s => {
      values.forEach(v => {
        let _s = s.replace(new RegExp(`{{${key}}}`, 'g'), v)
        result.push(_s)
      })
    })
    return result
  }
  keys.forEach(k => {
    let values = config[k] || []
    result = fn(result, k, values)
  })
  return result
}

/**
 * 日期格式化
 * @param {Date | string | number} date
 * @param {String} format
 */
const formatDate = (date, format = 'yyyy-MM-dd hh:mm:ss') => {
  if (new Date(date).toString() === 'Invalid Date') return date
  let result = format
  date = new Date(date)
  let dict = {
    'y+': date.getFullYear(),
    'M+': date.getMonth() + 1,
    'd+': date.getDate(),
    'h+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds()
  }
  Object.keys(dict).forEach(v => {
    if (new RegExp(`(${v})`).test(result)) {
      if (v === 'y+') {
        result = result.replace(new RegExp(v), dict[v].toString().substr(4 - RegExp.$1.length))
      } else {
        result = result.replace(new RegExp(v), RegExp.$1.length > 1 ? `00${dict[v]}`.substr(dict[v].toString().length) : dict[v])
      }
    }
  })
  return result
}

/**
 * 时间格式化
 * @param {Number} time
 * @param {String} format
 */
const formatTime = (time, format = 'dd天hh小时mm分ss秒') => {
  time = parseInt(time) || 0
  let result = format
  let handles = [
    {reg: 'd+', factor: 86400000},
    {reg: 'h+', factor: 3600000},
    {reg: 'm+', factor: 60000},
    {reg: 's+', factor: 1000},
  ]
  handles.forEach(v => {
    if (new RegExp(`(${v.reg})`).test(result)) {
      let val = Math.floor(time / v.factor)
      time = time % v.factor
      result = result.replace(new RegExp(v.reg), val.toString().padStart(2,0))
    }
  })
  return result
}

/**
 * 格式化文件大小
 * @param {Number} fileSize
 * @returns {String}
 */
const formatFileSize = (fileSize) => {
  fileSize = parseFloat(fileSize)
  // 若参数不合法则直接return
  if (isNaN(fileSize) || (fileSize <= 0)) { return '0 B' }
  let unitArr = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
  let index = 0
  let result = ''
  index = Math.floor(Math.log(fileSize) / Math.log(1024))
  result = fileSize / Math.pow(1024, index)
  // 保留的小数位数
  let decimals = (index === 0 ? 0 : 2)
  result = result.toFixed(decimals) + ' ' + unitArr[index]
  return result
}

/**
 * 格式化浮点数
 * @param {Number} num 要格式化的数字
 * @param {Number} n 小数位数
 * @param {String} separator 千分位分隔符
 * @returns {String}
 */
const formatDecimal = (num, n = 2, separator = '') => {
  let result = ''
  num = parseFloat(num)
  n = parseInt(n)
  n = n > 0 ? n : 0
  // 若参数不合法则直接return
  if (!isFinite(num)) return result
  result = num.toFixed(n)
  if (separator) {
    let l = result.split('.')[0].split('').reverse()
    let r = result.split('.')[1] || ''
    let t = ''
    for (let i = 0; i < l.length; i++) {
      t += l[i] + ((i + 1) % 3 === 0 && (i + 1) !== l.length ? ',' : '')
    }
    result = t.split('').reverse().join('') + '.' + r
  }
  return result
}

/**
 * 随机生成一个UUID
 */
const getUUID = () => {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
    return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16)
  })
}

/**
 * 字符串utf16转utf8
 * @param {String} str
 * @returns {String}
 */
const utf16to8 = (str) => {
  let result = ''
  let len = str.length
  for (let i = 0; i < len; i++) {
    let c = str.charCodeAt(i)
    if ((c >= 0x0001) && (c <= 0x007F)) {
      result += str.charAt(i)
    } else if (c > 0x07FF) {
      result += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F))
      result += String.fromCharCode(0x80 | ((c >> 6) & 0x3F))
      result += String.fromCharCode(0x80 | ((c >> 0) & 0x3F))
    } else {
      result += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F))
      result += String.fromCharCode(0x80 | ((c >> 0) & 0x3F))
    }
  }
  return result
}

/**
 * HEX转RGB
 * @param {String} hex '#99ff66'
 * @returns {Array} [R,G,B]
 */
const HEX2RGB = (hex) => {
  hex = hex.substr(1)
  let result = []
  for (let i = 0; i < 3; i++) {
    result[i] = parseInt(hex.substr(i * 2, 2), 16)
  }
  return result
}

/**
 * TXT转SVG
 * @param {Object}
 * @param {String} params/content 要转换的文本内容
 * @param {Number} params/width SVG图片宽度
 * @param {Number} params/height SVG图片高度
 * @param {Number} params/fontSize 字号
 * @param {String} params/fontFamily 字体
 * @param {String} params/color 文字颜色
 * @param {Number} params/opacity 透明度0-1
 * @param {Number} params/x 横轴图片中心百分比数（50代表50%）
 * @param {Number} params/y 纵轴图片中心百分比数（50代表50%）
 * @param {Number} params/rotate 旋转角度
 */
const TXT2SVG = (params) => {
  params = params || {}
  let content = params.content || ''
  let width = isNaN(parseFloat(params.width)) ? 300 : parseFloat(params.width)
  let height = isNaN(parseFloat(params.height)) ? 150 : parseFloat(params.height)
  let fontSize = isNaN(parseFloat(params.fontSize)) ? 14 : parseFloat(params.fontSize)
  let fontFamily = params.fontFamily || ''
  let color = params.color || '#a2a9b6'
  let opacity = isNaN(parseFloat(params.opacity)) ? 1 : parseFloat(params.opacity)
  let x = isNaN(parseFloat(params.x)) ? 50 : parseFloat(params.x)
  let y = isNaN(parseFloat(params.y)) ? 50 : parseFloat(params.y)
  let rotate = parseFloat(params.rotate) || 0
  let size = ` width="${width}" height="${height}"`
  let fill = ` fill="${color}"`
  let fillOpacity = ` fill-opacity="${opacity}"`
  let transform = ` transform="rotate(${rotate}, ${width / 2} ${height / 2})"`
  fontFamily = ` font-family="${fontFamily}"`
  if (color === '#000000') { fill = '' }
  if (opacity === 1) { fillOpacity = '' }
  if (rotate === 0) { transform = '' }
  let originSvg = `<svg${size} xmlns="http://www.w3.org/2000/svg"><text x="${x}%" y="${y}%" font-size="${fontSize}"${fill}${fillOpacity}${fontFamily}${transform} text-anchor="middle" dominant-baseline="middle">${content}</text></svg>`
  let encodeSvg = `data:image/svg+xml,${originSvg.replace(/\n/g, '').replace(/"/g,"'").replace(/%/g,'%25').replace(/#/g,'%23').replace(/{/g,'%7B').replace(/}/g,'%7D').replace(/</g,'%3C').replace(/>/g,'%3E')}`
  return {origin: originSvg, encode: encodeSvg}
}

/**
 * 相对地址转换绝对地址
 * @param {String} url 相对地址
 * @param {String} base 基准地址
 * @returns {String} 绝对地址
 */
const relativeUrl2absoluteUrl = (url, base) => {
  base = base || ''
  if (!base && location) {
    base = location.protocol + location.host
  }
  return new URL(url, base).href
}

/**
 * arrayBuffer转换为base64
 * @param {ArrayBuffer} data 数据
 * @param {String} mimeType
 * @returns {String} base64
 */
const arrayBuffer2base64 = (data, mimeType) => {
  let base64    = ''
  let encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  let bytes         = new Uint8Array(data)
  let byteLength    = bytes.byteLength
  let byteRemainder = byteLength % 3
  let mainLength    = byteLength - byteRemainder

  let a, b, c, d
  let chunk

  for (let i = 0; i < mainLength; i = i + 3) {
    chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]
    a = (chunk & 16515072) >> 18
    b = (chunk & 258048)   >> 12
    c = (chunk & 4032)     >>  6
    d = chunk & 63
    base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
  }

  if (byteRemainder === 1) {
    chunk = bytes[mainLength]
    a = (chunk & 252) >> 2
    b = (chunk & 3)   << 4
    base64 += encodings[a] + encodings[b] + '=='
  } else if (byteRemainder === 2) {
    chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]
    a = (chunk & 64512) >> 10
    b = (chunk & 1008)  >>  4
    c = (chunk & 15)    <<  2
    base64 += encodings[a] + encodings[b] + encodings[c] + '='
  }

  return `data:${mimeType};base64,${base64}`
}

/**
 * 字符串转换为base64
 * @param {String} data 字符串数据
 * @returns {String} base64
 */
const base64Encode = (data) => {
  let base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  let r, a, c, h, o, t
  for (c = data.length, a = 0, r = ''; a < c;) {
    if (h = 255 & data.charCodeAt(a++), a === c) {
      r += base64EncodeChars.charAt(h >> 2)
      r += base64EncodeChars.charAt((3 & h) << 4)
      r += '=='
      break
    }
    if (o = data.charCodeAt(a++), a === c) {
      r += base64EncodeChars.charAt(h >> 2)
      r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4)
      r += base64EncodeChars.charAt((15 & o) << 2)
      r += '='
      break
    }
    t = data.charCodeAt(a++)
    r += base64EncodeChars.charAt(h >> 2)
    r += base64EncodeChars.charAt((3 & h) << 4 | (240 & o) >> 4)
    r += base64EncodeChars.charAt((15 & o) << 2 | (192 & t) >> 6)
    r += base64EncodeChars.charAt(63 & t)
  }
  return r
}

/**
 * base64转换为字符串
 * @param {String} data base64数据
 * @returns {String} 字符串
 */
const base64Decode = (data) => {
  let base64DecodeChars = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1]
  let r, a, c, h, o, t, d
  for (t = data.length, o = 0, d = ''; o < t;) {
    do {
      r = base64DecodeChars[255 & data.charCodeAt(o++)]
    } while (o < t && r === -1)
    if (r === -1) break
    do {
      a = base64DecodeChars[255 & data.charCodeAt(o++)]
    } while (o < t && a === -1)
    if (a === -1) break
    d += String.fromCharCode(r << 2 | (48 & a) >> 4)
    do {
      if (c = 255 & data.charCodeAt(o++), 61 === c) return d
      c = base64DecodeChars[c]
    } while ( o < t && c === - 1 )
    if (c === -1) break
    d += String.fromCharCode((15 & a) << 4 | (60 & c) >> 2)
    do {
      if (h = 255 & data.charCodeAt(o++), 61 === h) return d
      h = base64DecodeChars[h]
    } while ( o < t && h === - 1 )
    if (h === -1) break
    d += String.fromCharCode((3 & c) << 6 | h)
  }
  return d
}

/**
 * 16进制字符串转换为base64
 * @param {String} data 16进制字符串数据
 * @returns {String} base64
 */
const hex2base64 = (data) => {
  return base64Encode(String.fromCharCode.apply(null, data.replace(/\r|\n/g, '').replace(/([\da-fA-F]{2}) ?/g, '0x$1 ').replace(/ +$/, '').split(' ')))
}

/**
 * base64转换为16进制字符串
 * @param {String} data base64数据
 * @returns {String} 16进制字符串
 */
const base642hex = (data) => {
  let str = base64Decode(data.replace(/[ \r\n]+$/, ''))
  let hex = []
  for (let i = 0; i < str.length; ++i) {
    let tmp = str.charCodeAt(i).toString(16);
    if (tmp.length === 1) {tmp = '0' + tmp};
    hex[hex.length] = tmp;
  }
  return hex.join('')
}

/**
 * 数组转字典
 * @param {Object} options 配置
 * @param {Array} options/data 数组数据
 * @param {String} options/key 生成字典所用key对应的字段名，默认取String(dataItem)
 * @param {Any} options/value 生成字典所用value对应的字段名，默认为dataItem
 */
 const list2dict = (options = {}) => {
  options = options || {}
  let result = {}
  let data = options.data || []
  data.forEach(v => {
    let key = v[options.key] === undefined ? String(v) : v[options.key]
    let value = options.value === undefined ? v : v[options.value]
    result[key] = value
  })
  return result
}

/**
 * 判断是否是身份证号码
 * @param {String} code 身份证号码
 * @returns {Boolean}
 */
const isCardID = (code) => {
  let result = true
  let tip = ''
  // 18位身份证需要验证最后一位校验位
  if (code.length === 18) {
    code = code.split('')
    //∑(ai×Wi)(mod 11)
    //加权因子
    let factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
    //校验位
    let parity = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2]
    let sum = 0
    let ai = 0
    let wi = 0
    for (let i = 0; i < 17; i++) {
      ai = code[i]
      wi = factor[i]
      sum += ai * wi
    }
    let last = parity[sum % 11]
    if (parity[sum % 11] != code[17]) {
      tip = '校验位错误'
      result = false
    }
  }else{
    result = false
  }
  return result
}

/**
 * 验证护照（包含香港、澳门）
 * @param {String} code 护照号码
 * @returns {Boolean}
 */
const isPassport = (value) => {
  return /(^[EeKkGgDdSsPpHh]\d{8}$)|(^(([Ee][a-fA-F])|([DdSsPp][Ee])|([Kk][Jj])|([Mm][Aa])|(1[45]))\d{7}$)/g.test(value)
}

/**
 * 验证统一社会信用代码
 * @param {String} value 统一社会信用代码
 * @returns {Boolean}
 */
const isCreditCode = (value) => {
  return /^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/g.test(value)
}

/**
 * 判断姓名是否合法
 * @param {String} value 姓名
 * @returns {Boolean}
 */
const isRealName = (value) => {
  return /^[\u4e00-\u9fa5a-zA-Z0-9·]+$/.test(value)
}

/**
 * 判断是否是手机号
 * @param {*} value 手机号
 * @returns {Boolean}
 */
const isMobileNumber = (value) => {
  return /^1[0-9]{10}$/.test(value)
}

/**
 * 判断金额格式是否正确
 * @param {*} value 金额
 * @returns {Boolean}
 */
const isAmount = (value) => {
  return /^[0-9]+([.]{1}[0-9]{1,2})?$/.test(value)
}

/**
 * 判断邮箱格式是否正确
 * @param {*} value 邮箱
 * @returns {Boolean}
 */
const isEmail = (value) => {
  return /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(value)
}

/**
 * 判断电话号码格式是否正确
 * @param {*} value 电话号码
 * @returns {Boolean}
 */
const isPhoneNumber = (value) => {
  return /^([0-9]{3,4}-)?[0-9]{7,8}$/.test(value)
}

/**
 * 验证车牌号(新能源+非新能源)
 * @param {*} value 车牌号
 * @returns {Boolean}
 */
const isLicensePlateNumber = (value) => {
  return /^(?:[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}(?:(?:[0-9]{5}[DF])|(?:[DF](?:[A-HJ-NP-Z0-9])[0-9]{4})))|(?:[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9 挂学警港澳]{1})$/g.test(value)
}

/**
 * 验证银行卡号（10到30位，覆盖对公/私账户，参考微信支付）
 * @param {*} value 银行卡号
 * @returns {Boolean}
 */
const isAccountNumber = (value) => {
  // value = value.replace(/\s+/g, '')
  return /^[1-9]\d{9,29}$/g.test(value)
}

/**
 * 校验URL是否正确
 * @param {*} value URL
 * @returns {Boolean}
 */
const isURL = (value) => {
  return /^http[s]?:\/\/.*/.test(value)
}

/**
 * 校验数据是否为ArrayBuffer
 * @param {*} value 数据
 * @returns {Boolean}
 */
const isArrayBuffer = (value) => {
  return (typeof ArrayBuffer === 'function') && (value instanceof ArrayBuffer || Object.prototype.toString.call(value) === '[object ArrayBuffer]')
}

/**
 * 判断某年是否是闰年
 * @param {*} year 年份
 * @returns {Boolean}
 */
const isLeapYear = (year) => {
  year = parseInt(year)
  // 若参数不合法则直接return
  if (isNaN(year)) { return '' }
  /**
   * 判断闰年条件:
   * 1.普通年能被4整除且不能被100整除的为闰年。如2004年就是闰年,1900年不是闰年
   * 2.世纪年能被400整除的是闰年。如2000年是闰年，1900年不是闰年
   */
  return !(year % (year % 100 ? 4 : 400))
}

/**
 * 判断某月有多少天
 * @param {*} month 月份
 * @param {*} isLeapYear 是否是闰年
 * @returns {Number} 天数
 */
const monthDaysCount = (month, isLeapYear) => {
  month = parseInt(month)
  // 若参数不合法则直接return
  if (isNaN(month)) { return '' }
  let result = 30
  if ([1, 3, 5, 7, 8, 10, 12].includes(month)) {
    result = 31
  } else if (month === 2) {
    result = isLeapYear ? 29 : 28
  }
  return result
}

/**
* 判断数据类型
* @param {*} param
*/
const typeOf = (param) => {
  return Object.prototype.toString.call(param).slice(8,-1)
}

/**
 * 简单深拷贝
 * @param {*} data
 */
const deepCopy = (data) => {
  let result = data
  try {
    result = JSON.parse(JSON.stringify(data))
  } catch (err) {
    console.log(err)
  }
  return result
}

/**
 * 深拷贝
 * @param {*} data
 */
const deepCopyV2 = (data) => {
  let dataType = typeOf(data)
  let result
  if (dataType ==='Array') {
    result = []
    for(let i = 0, len = data.length; i < len; i++){
      result.push(deepCopyV2(data[i]))
    }
  } else if (dataType ==='Object') {
    result = {}
    for(let key in data){
      result[key] = deepCopyV2(data[key])
    }
  } else {
    result = data
  }
  return result
}

/**
 * 克隆
 * @param {*} data
 */
const clone = deepCopyV2

/**
* 数组元素交换位置
* @param {Array} arr 数组
* @param {Number} index1 添加项目的位置
* @param {Number} index2 删除项目的位置
*/
const swapArray = (arr, index1, index2) => {
  arr[index1] = arr.splice(index2, 1, arr[index1])[0]
  return arr
}

/**
* 合并多个ArrayBuffer 或 TypeArray
* @param {Array} arr 数组
*/
const mergeArrayBuffer = (arrays) => {
  let totalLen = 0
  for (let i = 0; i < arrays.length; i++) {
    arrays[i] = new Uint8Array(arrays[i]) // 全部转成Uint8Array
    totalLen += arrays[i].length
  }
  let result = new Uint8Array(totalLen)
  let offset = 0
  for(let arr of arrays) {
    result.set(arr, offset)
    offset += arr.length
  }
  return result.buffer
}

/**
 * 版本号比较
 * @param {Array} versionArr1 版本1
 * @param {Array} versionArr2 版本2
 */
const compareVersions = (versionArr1, versionArr2) => {
  if (!(versionArr1 instanceof Array && versionArr2 instanceof Array)) return ''
  if (versionArr1.join('.') === versionArr2.join('.')) return 'equal'
  for(let index = 0; index < versionArr1.length; index++) {
    let versionItemA = Number(versionArr1[index]),
        versionItemB = Number(versionArr2[index])
    if (versionItemA > versionItemB) {
        return ['biggest','bigger','big','big'][index]
    } else if (versionItemA < versionItemB) {
        return 'small'
    }
  }
  return ''
}

/**
 * 生成指定范围内的随机数
 * @param {Number} min 下限
 * @param {Number} max 上限
 * @param {Boolean} decimal 是否为浮点数
 */
const random = (min, max, decimal) => {
  let result = Math.random() * (max - min) + min
  result = decimal ? result : parseInt(result)
  return result
}

/**
 * 生成随机数字字母字符串
 * @param {Number} len 字符串长度
 */
const randomString = (len) => {
  let result = ''
  while(result.length < len){
    result += Math.random().toString(36).substr(2)
  }
  return result.substr(result.length - len)
}

/**
 * 拆分数字
 * @param {Object} options 配置
 * @param {Number} options/total 要拆分的数字面额
 * @param {Number} options/count 拆分后的份数
 * @param {Number} options/min 每份的最小值
 * @param {Number} options/digits 小数位数
 * @param {Number} options/maxSplitCount 最多拆分几次
 * @returns {Array} 拆分后得到的数组
 */
const splitNumber = (options) => {
  let {total, count, min, digits, maxSplitCount} = options
  let result = []
  let splitCount = 0
  total = parseFloat(total)
  count = parseInt(count) || random(2, 10)
  digits = digits || 2
  min = min || 0
  maxSplitCount = maxSplitCount || count * 2
  if (!total || !count) {
    throw('total参数不合法')
  }
  if (count * min > total) {
    throw('count乘以min不应大于total')
  }
  for (let i = 0; i < count; i++) {
    result[i] = min
  }
  total -= min * count
  while (total > 0) {
    let value = (splitCount > maxSplitCount || total <= Math.pow(10, -digits)) ? total : parseFloat((Math.random() * total / 2).toFixed(digits))
    let index = splitCount % count
    result[index] = parseFloat((result[index] + value).toFixed(digits))
    total -= value
    splitCount++
  }
  return result
}

/**
 * 函数防抖 (只执行最后一次点击)
 * @param fn
 * @param delay
 * @returns {Function}
 * @constructor
 */
const debounce = (fn, t) => {
  let timer = null
  let delay = t || 500
  return function () {
    let context = this, args = arguments
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(context, args)
    }, delay)
  }
}

/**
 * 函数节流 (在interval时间段内只执行一次&多次触发延迟)
 * @param fn
 * @param interval
 * @returns {Function}
 * @constructor
 */
const throttle = (fn, t) => {
  let timer = null
  let t_start
  let interval = t || 1000
  return function () {
    let context = this, args = arguments, t_curr = +new Date()
    clearTimeout(timer)
    if (!t_start) { t_start = t_curr }
    if (t_curr - t_start >= interval) {
      fn.apply(context, args)
      t_start = t_curr
    } else {
      timer = setTimeout(function () {
        fn.apply(context, args)
      }, interval)
    }
  }
}

/**
 * 函数节流 (在interval时间段内只执行一次&多次触发丢弃)
 * @param fn
 * @param interval
 * @returns {Function}
 * @constructor
 */
const throttleV2 = (fn, t) => {
  let t_start
  let interval = t || 1000
  return function () {
    let context = this, args = arguments, t_curr = +new Date()
    if (!t_start || (t_curr - t_start >= interval)) {
      fn.apply(context, args)
      t_start = t_curr
    }
  }
}

export default module.exports = {
  // 第三方库
  axios,
  qs,
  // 工具函数
  ajaxConstructor,
  ajax,
  ajaxJson,
  ajaxError,
  downloadInstance,
  checkUrlStatus,
  enumStringByTemplate,
  jsonParse,
  formatDate,
  formatTime,
  formatFileSize,
  formatDecimal,
  getUUID,
  utf16to8,
  HEX2RGB,
  TXT2SVG,
  relativeUrl2absoluteUrl,
  arrayBuffer2base64,
  base64Encode,
  base64Decode,
  hex2base64,
  base642hex,
  list2dict,
  encodeGb2312,
  decodeGb2312,
  isCardID,
  isPassport,
  isCreditCode,
  isRealName,
  isMobileNumber,
  isAmount,
  isEmail,
  isPhoneNumber,
  isLicensePlateNumber,
  isAccountNumber,
  isURL,
  isArrayBuffer,
  isLeapYear,
  monthDaysCount,
  cronValidate,
  typeOf,
  deepCopy,
  deepCopyV2,
  clone,
  swapArray,
  mergeArrayBuffer,
  compareVersions,
  random,
  randomString,
  splitNumber,
  debounce,
  throttle,
  throttleV2
}
